﻿#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<string.h>
//main函数的多种写法
//int main(void)//void的意思是main函数没有参数
//{
//	return 0;
//}
//
//int main()
//{
//	return 0;
//}
//
//int main(int argc,char* argv[])
//{
//	return 0;
//}

//打印字符
//printf("%c\n", 100);
//
////打印小数（注意两种写法）
//printf("%lf\n", 3.5);//double类型
//printf("%f\n", 3.5f);//float类型(小数点后默认打印6位）

//%u   打印无符号整数
//%c   字符
//%s     字符串
//%p     地址
//%zd    size_t
//%Lf    long double

//%c特殊之处
//int main()
//{
//	int a = 10;
//	scanf("%d", &a);//输入_____100
//	printf("%d\n", a);//输出100(略过空格输出）
//	char ch = '0';
//	scanf("%c" ，&ch);//___abc
//	printf("%d\n", ch);//无（说明空格也是字符，除%c以外，其余都会自动忽略空格
//
//	return 0;
//}

//"\0"是字符串的结束标志
//int main()
//{
//	printf("%s", "abc");
//	return 0;
//}
//"\0""\n"都是转义字符
//特殊转义字符
//\ddd:ddd表示1~3个八进制数字      如：\130表示转义字符
//\xdd:dd表示两个十六进制数字          \x30表示字符0；
//
//
//strlen("abc");
//strlen()求字符串的长度，需包含于头文件#include<string.h>
//
//布尔类型
//_bool或者bool    //布尔类型是专门用来表示真假
//#define bool _bool
//#define false 0
//#define true 1
//
//int main()
//{
//	_bool flag = 0;
//	bool flag2;
//	if (flag)
//	{
//		printf("hehe\n");
//	}
//	return 0;
//}
//
//1Byte = 8bit
//1KB = 1024Byte
//1MB = 1024KB
//
//
//sizeof(求字节的大小）
// sizeof的计算结果是size_t类型
//int main()
//{
//	int a = 10;
//	printf("%zd\n", sizeof(a));
//	return 0;
//}

//sizeof后面的表达式是不真实参与运算的，根据表达式的类型来得出大小
//int main()
//{
//	int a = 10;
//	short s = 4;
//	printf("%zd\n", sizeof(s = a + 5));//2（short类型为两个字节的大小）
//	printf("%d\n", s);//4
//	return 0;
//}


//整型变量声明为unsigned的好处是，同样长度的内存增大了1倍
//char到底是signed char还是unsigned char，这取决于编译器

//当全局变量与局部变量同名时，局部变量优先
//int a = 10;
//int main()
//{
//	int a = 2;
//	printf("%d\n", a);
//	return 0;
//}


//除号的两端如果是整数，执行的是整数除法，得到的结果也是整数。如果希望得到浮点数的结果，两个运算数至少有一个浮点数
//int main()
//{
//	printf("%d\n", 6 / 4);//1
//	printf("%f\n", 6 / 4.0);//1.500000
//	return 0;
//}
//
//
////负数求模规则是，结果的正负号由第一个运算数的正负号决定
//int mian()
//{
//	printf("%d\n", 11 % 5);//1
//	printf("%d\n", -11 % 5);//-1
//	return 0;
//}


//前置++
//int main()
//{
//	int a = 10;
//	int b = ++a;//前置++（先+1，在使用）
//	//a = a+1,b=a
//	int b = a++;//后置++（先使用，后+1）
//	a = 10;
//	printf("%d/n", a++);//10
//	printf("%d/n", a);//11
//	return 0;
//}
//
//int main()
//{
//	int a = (int)3.14;//强制类型转换
//	printf("%d\n", a);//3
//	return 0;
//}
//
//int main()
//{
//	printf("%5d\n", 123);//__123右对齐
//	printf("%-5d\n", 123);//123__左对齐
//	return 0；
//}
//
////对小数
//int main()
//{
//	printf("%15f\n", 123.45);//_____123.450000(小数点后面的6位是固定的）
//	return 0;
//}
//
//int main()
//{
//	printf("%2f\n", 0.5);//0.50
//	return 0;
//}

//%s,它的规则是，从当前第一个非空的字符开始读起，直到遇到空白字符为止
//int main()
//{
//	char arr[10] = { 0 };//___abc__def
//	scanf("%s", arr);//arr不需要地址，本身就是地址
//	//scanf()将字符读入字符数组时，不会检测字符串是否超过了数组长度，所以，储存字符串时，很可能会超过数组的边界，导致预想不到的结果，为了防止这种情况，使用%s占位符时，应该指定读入
//	//字符串的最长长度
//	printf("%s", arr);//abc
//	return 0;
//}

//%.[m]s
//int main()
//{
//	printf("%.3s\n", hello world);//hel
//	return 0;
//}




//scanf的返回值（scanf返回值是它读取字符的个数）
//如果没有读取任何项，或者匹配失败，则返回0。如果在成功读取任何数据之前，发生了读取错误或者遇到读取到文件末尾，则返回常量EOF
//int main()
//{
//	int a = 0;
//	int b = 0;
//	int c = 0;
//	int ret = scanf("%d%d%d", &a, &b, &c);//输入20 10 30
//	printf("ret = %d\n", ret);//ret = 3
//	printf("a =%d b=%d c=%d\n", a, b, c);//a = 20 b=10 c=30
//	return 0;
//}
//
////多组输入
//int main()
//{
//	int a = 0;
//	int b = 0;
//	while (scanf("%d %d", &a, &b) == 2)//或者while(scanf("%d %d", &a, &b) != EOF)
//	{
//		int c = a + b;
//		printf("%d\n", c);
//	}
//	return 0;
//}


//判断相等，把常量放在左边，即使误写成3 = a,它会报错
//int main()
//{
//	int a = 0;
//	scanf("%d", &a);
//	if (3 == a)
//	{
//		printf("hh");
//	}
//	return 0;
//}

//三目操作符
//exp1?exp2:exp3
//exp1结果为真，执行exp2,exp2的结果是整个表达式的结果
//exp1结果为假，执行exp3, exp3的结果是整个表达式的结果

//int main()
//{
//	int a = 0;
//	int b = 0;
//	scanf("%d", &a);
//	if (a > 5)
//		b = 3;
//	else
//		b = -3;
//	//改用三目操作符
//	b = (a > 5 ? 3 : -3);
//		printf("%d\n",b);
//
//		return 0;
//}
//
//
////逗号表达式：会从从左向右依次计算，但是整个表达式的结果是最后一个表达式的结果
//int main()
//{
//	int b = 0;
//	int c = 4;
//	//逗号表达式：会从从左向右依次计算，但是整个表达式的结果是最后一个表达式的结果
//	int a = (a = 3, a + 3, b = c * 3, c = 2 * a + b);
//	return 0;
//}


//switch (表达式)
//{
//case 1:statement;
//case 2:statement;
//default:statement;
//}

//输入任何整数，求出其模3的值
//int main()
//{
//	int num = 0;
//	scanf("%d", &num);
//	switch (num%3)//switch后面必须是整型表达式
//{
//	case 0:
//		printf("余数是0\n");
//		break;
//case 1:
//	printf("余数是1\n");
//	break;
//
//case 2:
//	printf("余数是2\n");
//	break;
//
//default:
//	break;
//}
//
//	return 0;
//}


//输入一个整数，逆序打印这个整数的每一位
//int main()
//{
//	int n = 0;
//	scanf("%d", &n);
//	while (n)
//	{
//		printf("%d ", n % 10);
//		n /= 10;
//	}
//	return 0;
//}


//break和continue语句
//break永久终止循环，一个break只能跳出一层循环
//continue的作用是跳过本次循环的continue后面的代码，直接去判断部分，看是否进行下一次判断



//素数（可以被1和自身整除的数）
//打印100~200的素数
//#include<stdio.h>
//int main()
//{
//	int i = 0;
//	for (int i = 100; i < 200; i++)
//	{
//		int flag = 1;
//		int j = 0;
//		for (j = 2; j < i - 1; j++)
//		{
//			if (0 == i % j)
//			{
//				flag = 0;
//				break;
//			}
//		}
//		if (1 == flag)
//			printf("%d ", i);
//	}
//	return 0;
//}
//
//
////goto语句
////注意：goto语句只能在本函数内部使用
//int main()
//{
//again:
//	printf("hehe\n");
//	goto again;//again可以随便起
//	return 0;
//}
//尽量少使用goto语句


//数组
// 数组在内存中是连续存放的
// 随着下标的增长，地址是由小到大
//求数组元素个数
//int main()
//{
//	int arr[10] = { 0 };
//	printf("%zd\n", sizeof(arr) / sizeof(arr[1]));//求数组元素个数
//	return 0;
//}



//变长数组（C99）
//C99之前数组的大小只能使用常量或常量表达式指定
//C99中引入了变长数组的概念
//变长数组的意思是数组的大小可以使用变量来指定，但是数组一旦创建后，大小就无法改变了
// //vs不支持变长数组，但是这样写的
//int main() {
//	int n = 10;
//	//int arr[n];//变长数组不能初始化
//	for (int i = 0; i < n; i++)
//	{
//		scanf("%d\n", &arr[i]);
//	}
//	return 0;
//}
//


//多个字符从两边移动，向中间汇聚
//int main()
//{
//	char arr1[] = "Welcome to China!";
//	char arr2[] = "*****************";
//
//	int left = 0;
//	//求字符串的长度用strlen,strlen统计的是政府产\0之前字符的个数。//求字符的大小用sizeof
//	int right = strlen(arr1) - 1;
//	                          
//	while (left <= right)
//	{
//		arr2[left] = arr1[left];
//		arr2[right] = arr1[right];
//		printf("%s\n", arr2);
//		Sleep(1000);//Sleep函数，休眠，单位是毫秒；
//		system("cls");
//		left++;
//		right--;
//	}
//	printf("%s\n", arr2);
//	return 0;
//}

//二分查找(明白二分查找的原理）
//在有序的数据中查找某位数
//#include<stdio.h>
//int main()
//{
//	int arr[17] = { 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17 };
//	int sz = sizeof(arr) / sizeof(arr[1]);
//	int left = 0;
//	int right = sz - 1;
//	int k = 7;
//	int flag = 1;
//	while (left <= right)
//	{
//		int mid = (left + right) / 2;
//
//		if (arr[mid] > k)
//		{
//			right = mid - 1;
//		}
//		else if (arr[mid] < k)
//		{
//			left = mid + 1;
//		}
//		else
//		{
//			flag = 0;
//			printf("找到了，下标为%d\n", mid);
//			break;             
//		}
//	}
//	if (flag == 1)
//	{
//		printf("找不到了");
//	}
//	return 0;
//}


//return后面什么都没有，直接写return，这种写法适合返回类型是void的情况
//void test(int n)
//{
//	if (n <= 0)
//		return;
//
//}
//
//int main()
//{
//	test(5);
//	return 0;
//}

//return返回的值和函数返回类型不一致，系统会自动将返回的值隐式转换为函数的返回类型
//int test(int n)
//{
//	return 3.14;
//}
//int main()
//{
//	int a = test(4);
//	printf("%d\n", a);
//	return 0;
//}

//计算某年某月有多少天？
//
//int is_leap_year(int y)
//{
//	if ((0 == y % 4) && (0 != y % 100) || 0 == y % 400)
//		return 1;
//	else
//		return 0;
//}
//int get_days_of_month(int y, int x)
//{
//	//判断润年
//	int days[] = { 0,31,28,31,30,31,30,31,31,30,31,30,31 };
//	int day = days[x];
//	if (is_leap_year(y) && x == 2)
//		day += 1;
//	return day;
//
//}
//int main()
//{
//	int year = 0;
//	int month = 0;
//	scanf("%d %d", &year, &month);
//	int days = get_days_of_month(year, month);
//	printf("%d\n", days);
//	return 0;
//}


//链式访问（将一个函数的返回值作为另外一个函数的参数）
//#include<string.h>
//int main()
//{
//	printf("%d\n", strlen("abcd"));
//	return 0;
//}
//
////static和extern
////static 和extern 都是C语言中的关键字。
////static 是 静态的 的意思，可以用来：
////修饰局部变量
////修饰全局变量
////修饰函数
//
////extern 是用来声明外部符号的。
//void test()
//{
//	int i = 0;
//	i++;
//	printf("%d ", i);//1 1 1 1 1
//	//test函数内部的i是局部变量，每次进入函数的时候，i会创建，出函数的时候销毁
//}
//int main()
//{
//	int i = 0;
//	for (i = 0; i < 5; i++)
//	{
//		test();
//	}
//	return 0;
//}
//
////static修饰局部变量
//void test()
//{
//	//static修饰局部变量 
//	static int i = 0;
//	i++;
//	printf("%d ", i);//1 2 3 4 5
//}
//int main()
//{
//	int i = 0;
//	for (i = 0; i < 5; i++)
//	{
//		test();
//	}
//	return 0;
//}
////结论：static修饰局部变量改变了变量的生命周期，生命周期改变的本质是改变了变量的存储类型，本
////来1个局部变量是存储在内存的栈区的，但是被 static 修饰后存储到了静态区。存储在静态区的变
////量和全局变量是一样的，生命周期就和程序的生命周期一样了，只有程序结束，变量才销毁，内存才
////回收
////
//
//
////extern是用来声明外部符号的
//
////假设add.c
////int g_val = 2023;
//
////test.c
//extern int g_val;
//int main()
//{
//	printf("%d\n", g_val);//2023
//	return 0;
//}
//
//
////假设add.c
////static int g_val = 2028;
//
////test.c
//extern int g_val;
//int main()
//{
//	printf("%d\n", g_val);//报错
//	return 0;
//}

//结论：
//⼀个全局变量被static修饰，使得这个全局变量只能在本源⽂件内使⽤，不能在其他源⽂件内使⽤。
//本质原因是全局变量默认是具有外部链接属性的，在外部的⽂件中想使⽤，只要适当的声明就可以使⽤；
// 但是全局变量被 static修饰之后，外部链接属性就变成了内部链接属性，只能在⾃⼰所在的源⽂件内部使⽤了，其他源⽂件，即使声明了，也是⽆法正常使⽤的。
//使⽤建议：如果⼀个全局变量，只想在所在的源⽂件内部使⽤，不想被其他⽂件发现，就可以使⽤static修饰。






//指针（访问内存的钥匙）
//1.CPU访问内存中的某个字节空间，必须知道这个字节空间在内存的什么位置，⽽因为内存中字节很多，所以需要给内存进⾏编址(就如同宿舍很多，需要给宿舍编号⼀样)。
//2.计算机中的编址，并不是把每个字节的地址记录下来，⽽是通过硬件设计完成的。
//3.地址信息被下达给内存，在内存上，就可以找到该地址对应的数据，将数据在通过数据总线传⼊CPU内寄存器
//
//int main()
//{
//	int a = 10;
//	printf("%p\n", &a);
//	int* p = &a;//p是一个指针变量，开空间//
////类型是int*；//具体于指针图
//	return 0;
//}
//
//*——解引用操作符（间接访问操作符）
//int main()
//{
//	int a = 10;
//	int* p = &a;
//	*p = 0;//相当于a = 0;//理解：&a是a的地址，*p是*（&a）解引用&a,相当于a
//
//	return 0;
//}
//
//
//
//指针变量的大小
//指针变量的⼤⼩取决于地址的⼤⼩ 
//int main()
//{
//	printf("%zd\n", sizeof(char*));
//	printf("%zd\n", sizeof(short*));
//	printf("%zd\n", sizeof(int*));
//	printf("%zd\n", sizeof(double*));
//	return 0;
//}
//
//结论：(如指针图）
//• 32位平台下地址是32个bit位，指针变量⼤⼩是4个字节
//• 64位平台下地址是64个bit位，指针变量⼤⼩是8个字节
//• 注意指针变量的⼤⼩和类型是⽆关的，只要指针类型的变量，在相同的平台下，⼤⼩都是相同的。
//
//
//指针变量的意义：
//1.指针类型决定了指针进行解引用操作符的时候访问几个字符，也就是决定指针的权限（如指针图）
//2.我们可以看出， char* 类型的指针变量+1跳过1个字节， int* 类型的指针变量+1跳过了4个字节。这就是指针变量的类型差异带来的变化。(如图）
//int main()
//{
//	int a = 0x11223344;
//	int* pa = &a;
//	*pa = 0;
//	return 0;
//}
//
//int main()
//{
//	int a = 0x11223344;
//	char* pa = &a;
//	*pa = 0;
//	return 0;
//}
//int main()
//{
//	int a = 10;
//	int* pa = &a;
//	char* pc = &a;
//	printf("pa     = %p\n", pa);
//	printf("pa + 1 = %p\n", pa+1);
//	printf("pc     = %p\n", pc);
//	printf("pa + 1 = %p\n", pc + 1);
//
//	return 0;
//}
//
//
//
//
//void* 无具体类型的指针(void* 可以接受任意类型的指针）
//局限：void*类型不可以进行加减运算
//int main()
//{
//	int a = 0;
//	float f = 0.0;
//	void* p = &a;
//	p = &f;
//	return 0;
//}
//⼀般 void* 类型的指针是使⽤在函数参数的部分，⽤来接收不同类型数据的地址，这样的设计可以实现泛型编程的效果。使得⼀个函数来处理多种类型的数据
//
//
//const修饰变量
//int main()
//{
//	/*int a = 10;
//	a = 20*/;//a会被改变
//	//若不想让a被修改
//		const int a = 20;
//	//a = 40;//报错
//		//在C++const修饰的a是常量
//		//在C语言const修饰的a是变量
//	return 0;
//
//}
//
//const修饰指针
//int main()
//{
//	int a = 10;
//	int b = 20;
//	/*int* p = &a;
//	int* const p = &a;*///const修饰指针变量时，放在*的左边，限制的是:指针指向的内容，不能通过指针来修改指向的内容，但是可以修改指针变量本身的值
//	//p = &b;//err
//	//int* pi = &a;
//	int const* pi = &a;////const放在*右边的时候，限制的是p变量的本身，指针不能再指向其他变量，但是可以通过指针变量，修改指针变量的内容
//	pi = &b;
//	//*pi = 100;//err
//	
//	return 0;
//}
//
//结论：const修饰指针变量的时候
//• const如果放在* 的左边，修饰的是指针指向的内容，保证指针指向的内容不能通过指针来改变。
//但是指针变量本⾝的内容可变。
//• const如果放在* 的右边，修饰的是指针变量本⾝，保证了指针变量的内容不能修改，但是指针指
//向的内容，可以通过指针改变。
//
//
//
//
//野指针（野指针是指针指向的位置是不可知的）
//1.指针未初始化
//int main()
//{
//	//正确
//	/*int a = 10;
//	int* p = &a;
//	*p = 20;*/
//
//	int* p;//p是一个局部指针变量，默认为随机值
//	*p = 20;
//	return 0;
//}
//
//2.指针越界
//int main()
//{
//	int arr[10] = { 1,2,3,4,5,6 };
//	int* p = arr;
//	for (int i = 0; i <= 10; i++)//i = 10,指针越界
//	{
//		p++;
//	}
//	return 0;
//}
//
//3.指针指向的空间释放
//int* test()
//{
//	int a = 10;
//	return &a;//函数栈帧的创建和销毁
//}
//
//int main()
//{
//	int* p = test();
//	printf("%d", *p);
//}
//
//
//规避野指针
//1.如果知道指针应该指向哪里，就给初始化一个明确的地址，如果不知道指针指向哪里，置空
//2.指针不在使用时，应置为NULL;
//
//
//
//assert断言
//assert出现错误的时候，直接会报错，指明在什么文件，在哪一行
//
//
//
//传值调用：实参传递给形参的时候，形参会单独创建⼀份临时空间来接收实参，对形参的修改不影响实参。
//传址调⽤，可以让函数和主调函数之间建⽴真正的联系，在函数内部可以修改主调函数中的变量；所以未来函数中只是需要主调函数中的变量值来实现计算，就可以采⽤传值调⽤。如果函数内部要修改主调函数中的变量的值，就需要传址调⽤。
//交换两个整数的
//
//不可传
//void sweep(int x, int y)
//{
//	int tmp = x;
//	x = y;
//	y = tmp;//形参是实参的一份临时拷贝，对形参的修改不会改变实参
//}
//int main()
//{
//	int a = 10;
//	int b = 20;
//	sweep(a,b);//传变量本身，传值调用
//	return 0;
//}
//
////可传
//void sweep2(int* x, int* y)
//{
//	int tmp = *x;
//	*x = *y;
//	*y = tmp;
//}
//int main()
//{
//	int a = 10;
//	int  b = 20;
//	sweep2(&a, &b);
//	return 0;
//}
//
//
//
//数组名的理解（图）
//数组名是数组首元素的地址
//
//两个列外
//• sizeof(数组名)，sizeof中单独放数组名，这⾥的数组名表⽰整个数组，计算的是整个数组的⼤⼩，
//单位是字节
//•& 数组名，这⾥的数组名表⽰整个数组，取出的是整个数组的地址（整个数组的地址和数组⾸元素
//的地址是有区别的）
//除此之外，任何地⽅使⽤数组名，数组名都表⽰⾸元素的地址。
//
//
//一维数组传参的本质
//数组传参的时候，形参是可以写出数组的形式，但是本质上还是指针变量
//1.数组传参的本质是传递了数组首元素的地址，所以形参访问的数组和实参的数组是同一个数组。
//2.形参的数组是不会单独再创建数组空间的，所以形参的数组是可以省略掉数组大小的
//Print(int arr[], int x)//或者（int *p,int x)
//{
//	for (int i = 0; i < x; i++)
//	{
//		printf("%d", arr[i]);
//	}
//}
//int main()
//{
//	int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
//	int sz = sizeof(arr) / sizeof(arr[0]);//一定要写在主函数里面的
//	Print(arr, sz);
//	return 0;
//}
//
//总结：⼀维数组传参，形参的部分可以写成数组的形式，也可以写成指针的形式。
//
//
//二级指针(图）
//int main()
//{
//	int a = 10;
//	int** pp = &p;
//	return 0;
//}
//
//
//指针数组（存放指针的数组）（如果不懂，自己找视频）
//int main()
//{
//	int arr1[] = { 1,2,3 };
//	int arr2[] = { 2,3,4 };
//	int arr3[] = { 3,4,5 };
//	int* arr[] = { arr1,arr2,arr3 };
//	return 0;
//}
//
//
//
//字符指针变量
//
//int main()
//{
//	char ch = 'w';//ok
//	char* pc = &ch;
//
//	char arr[10] = "abcdef";//ok
//	char* p1 = arr;
//
//	const char* p = "abcdef";//ok//注意：常量字符串是不能被修改的
//
//	printf("%s", p);//%s打印字符串时，需要提供首地址
//
//	return 0;
//}
//
//
//
////数组指针变量（存放的是数组的地址，指向数组的指针变量）有些数组也需要指针，int (*p)arr[4]//类型int (*)arr[4]
//int main()
//{
//	int arr[10] = { 1 };
//	int* p1[10];//p1是指针数组——存放指针的数组
//	int(*p2)[10];//p2是指针变量，指向数组，类型是int(*)[10]——数组指针变量
//	int(*p2)[10] = &arr;
//
//	return 0;
//}
//
//void print(int(*arr)[5], int r, int c)//指向一维数组的指针
//{
//	for (int i = 0; i < r; i++)
//	{
//		for (int j = 0; j < c; j++)
//		{
//			printf("%d ", *(*(arr + i) + j));//arr[i][j]
//		}
//		printf("\n");
//	}
//
//}
//
////数组指针变量可用作二维数组的传参中的形参
////先思考一个问题
////二维数组的数组名也是数组首元素的地址，到底是谁的地址？
////首元素的地址就是第一行的地址
//int main()
//{
//	int arr[3][5] = { {1,2,3,4,5},{2,3,4,5,6},{3,4,5,6,7} };
//	printf(arr, 3, 5);
//	return 0;
//}
////总结：⼆维数组传参，形参的部分可以写成数组，也可以写成指针形式。
//
//int Add(int x,int y)
//{
//	return x + y;
//}
////函数指针变量（指向函数的）
//int main()
//{
//	printf("%p", Add);
//	printf("%p", &Add);//函数名和&函数名地址一样，没有区别
//	int(*p)(int,int) = &Add;//p函数指针变量//类型是：int(*)(int,int)
//
//
//	int d = Add(2, 3);
//	int f = p(4, 5);//写法不一样，功能一样
//	int e = (*p)(7, 9);
//	return 0;
//}
//
//int Add(int x, int y)
//{
//	return x + y;
//}
//int Sub(int x, int y)
//{
//	return x - y;
//}
//int Mul(int x, int y)
//{
//	return x * y;
//}
//int Diy(int x, int y)
//{
//	return x / y;
//}
////函数指针数组
//int main()
//{
//	int(*pf1)(int, int) = Add;//pf1是函数指针变量
//	int(*pfarr[4])(int, int) = { Add,Sub,Mul,Diy };//pfarr是函数指针数组
//	return 0;
//
//
//
//回调函数
///*回调函数就是⼀个通过函数指针调⽤的函数。
//如果你把函数的指针（地址）作为参数传递给另⼀个函数，当这个指针被⽤来调⽤其所指向的函数
//时，被调⽤的函数就是回调函数。回调函数不是由该函数的实现⽅直接调⽤，⽽是在特定的事件或条
//件发⽣时由另外的⼀⽅调⽤的，⽤于对该事件或条件进⾏响应*/
//
//
//int add(int a, int b)
//{
//	return a + b;
//}
//int sub(int a, int b)
//{
//	return a - b;
//}
//int mul(int a, int b)
//{
//	return a * b;
//}
//int div(int a, int b)
//{
//		return a / b;
//}
//	void calc(int(*pf)(int, int))
//	{
//		int ret = 0;
//		int x, y;
//		printf("输⼊操作数：");
//		scanf("%d %d", &x, &y);
//		ret = pf(x, y);
//		printf("ret = %d\n", ret);
//	}
//	int main()
//	{
//		int input = 1;
//		do
//		{
//			printf("******************");
//			printf(" 1:add 2:sub" );
//			printf(" 3:mul 3.div" );
//			printf("******************");
//			printf("请选择：");
//			scanf("%d", &input);
//			switch (input)
//			{
//			case 1:
//				calc(add);
//				break;
//			case 2:
//				calc(sub);
//				break;
//			case 3:
//				calc(mul);
//				break;
//			case 4:
//				calc(div);
//				break;
//			case 0:
//				printf("退出程序\n");
//				break;
//			default:
//				printf("选择错误\n");
//				break;
//			}
//		} while (input);
//		return 0;
//	}
//
//qsort用来排序(需头文件#include<stdlib.h>)
//1.库函数，直接可以用来排序数据
//2.底层使用快速排序的方式
//3.可以排任何类型
// void asort(void * base,//指针，指向的是待排序的数组的第一个元素
//            size_t num,//是base指向的待排序数组的元素个数
//            size_t size,//base指向的是待排序数组的元素的大小
//            int(*compar)(const void*,const void*)//函数指针   指向两个元素的比较函数
//            );
//
// 
//写一个冒泡排序的函数，对一组整型数组进行排序，排序为升序
//局限性：只能排整型
//void sort(int arr[],int sz)
//{
//	for (int i = 0; i < sz - 1; i++)
//	{
//		for (int j = 0; j <sz-1-i ; j++)
//		{
//			if(arr[j]>arr[j+1])
//			{
//				int tmp = arr[j];
//				arr[j] = arr[j + 1];
//				arr[j + 1] = tmp;
//			}
//			
//		}
//	}
//
//}
//int main()
//{
//	int arr[] = { 9,8,7,6,5,4,3,2,1,0 };
//	int sz = sizeof(arr) / sizeof(arr[0]);
//	sort(arr, sz);
//}
//
//
//注意：这段代码的写法
//int cmp_int(const void* p1, const void* p2)
//{
//	/*if (*(int*)p1 > *(int*)p2)
//		return 1;
//	else if (*(int*)p1 == *(int*)p2)
//		return 0;
//	else
//		return -1;*/
//	return *(int*)p1 - *(int*)p2;//简单写法
// //> ——》 >0
// //< ——》 <0
// //== ——》 ==0
//}
//void test1()
//{
//	int arr[] = { 9,8,7,6,5,4,3,2,1,0 };
//	int sz = sizeof(arr) / sizeof(arr[0]);
//	qsort(arr, sz, sizeof(arr[0]), cmp_int);
//}
//int main()
//{
//	test1();
//	return 0;
//}
//
//
//
//qsort比较结构体的大小
//首先，先想一下结果体的大小一个如何比较
//#include<stdlib.h>
//struct stu
//{
//	char name[20];
//	int age;
//};
////按照名字比
//int cmp_stu_by_name(const void* e1, const void* e2)
//
//{
//	return strcmp(((struct stu*)e1)->name, ((struct stu*)e2)->name);////strcmp - 是库函数，是专⻔⽤来⽐较两个字符串的⼤⼩的 ,需头文件#include<string.h>
//
//}
//
//int cmp_stu_by_age(const void* e1, const void* e2)
//{
//	return ((struct stu*)e1)->age - ((struct stu*)e2)->age;
//
//}
//void test()
//{
//	struct stu arr[3] = { {"zhangsan",20},{"list",35},{"wangwu",56} };
//	int sz = sizeof(arr) / sizeof(arr[0]);
//	qsort(arr,sz,sizeof(arr[0]),cmp_stu_by_name);
//	qsort(arr, sz, sizeof(arr[0]), cmp_stu_by_age);
//
//
//}
//int main()
//{
//	test();
//
//	return 0;
//}
//
//
//qsort的模拟实现？
//（指针末尾）
//
//
//sizeof和strlen的对比
//sizeof(计算的是使⽤类型创建的变量所占内存空间的⼤⼩。)
//sizeof括号里面有表达式的话，表达式是不参与计算的。
//
//strlen
//strlen 是C语⾔库函数，功能是求字符串⻓度.strlen 函数会⼀直向后找 \0 字符，直到找到为⽌，所以可能存在越界查找。
//





//函数递归
//递(递推）归（回归）：函数自己调用自己
//思想：大事化小
// 递归的特点：使用少量的代码，完成复杂的问题
//递归存在限制条件
//举例：理解递归的限制条件
//这个例子要写博客
//这个例子，递归的继续的条件是n>0,停下来的条件是n = 0;

//#include<stdio.h>
//int test(n)
//{
//	
//	if (0 == n)
//	return 	1;
//	else
//	return n * test(n - 1);
//}
//int main()
//{
//	int n = 0;
//	
//	scanf("%d", &n);
//	int ret = test(n);
//	printf("%d", ret);
//	return 0;
//}
//
//递归中，只要有函数调用，就会分配空间，递归会消耗一定的空间，还要注意递归是否栈溢出（stackoverflow)


//递归与迭代
//如果不想使用递归，就得想其他的方法，通常就是迭代的方式（通常就是循环的方式）

//求n的阶乘（换一种方式）
//#include<stdio.h>
//int fun(n)
//{
//	int Ret = 1;
//	for (int i = 1; i <=n; i++)
//	{
//		Ret *= i;
//	}
//	return Ret;
//}
//int main()
//{
//	int n = 0;
//	scanf("%d", &n);
//	int ret = 1;
//	ret = fun(n);
//	printf("%d", ret);
//	return 0;
//}


 //递归只要涉及计算的数值比较大，也不是很大吧，50或者100，递归效率就太低了


//计算第n个斐波拉契竖列
//
//#include<stdio.h>
//int fun(n)
//{
//	int a = 1;
//	int b = 1;
//	int c = 1;
//	while (n >= 3)
//	{
//		c = a + b;
//		a = b;
//		b = c;
//		n--;
//	}
//	return c;
//}
//int main()
//{
//	int n = 0;
//	scanf("%d", &n);
//	int ret = 1;
//	ret = fun(n);
//	printf("%d", ret);
//	return 0;
//}

//#include<stdio.h>
//int main()
//{
//	int n = 0;
//	scanf("%d", &n);
//	int ret = 1;
//	if (n <= 1)
//	{
//		ret = 1;
//		printf("%d\n", ret);
//	}
//	else
//	{
//		for (int i = 2; i <= n; i++)
//		{
//			ret *= i;
//		}
//		printf("%d\n", ret);
//
//	}
//
//	return 0;
//}


//strcpy用于拷贝字符串
#include <stdio.h>
#include <string.h>
#include <assert.h>

//void my_strcpy(char* dest, char* src)
//{
//	assert(src != NULL);
//	char ret = *dest;
//	while (*src != '\0')
//	{
//		*dest = *src;
//		dest++;
//		src++;
//	}
//	*dest = *src;
//	return ret;
//
//}

//改进版
//void my_strcpy(char* dest, char* src)
//{
//	assert(src != NULL);
//	char ret = *dest;
//	while (*dest++ = *src++)
//	{
//		;
//	}
//	return ret;
//
//}


//int main()
//{
//	//int a = 10;
//	//int b = 20;
//	//b = a;
//	char arr1[] = "abcdef";
//	char arr2[20] = { 0 };
//	//arr2 = arr1;//不行的，为什么呢？因为数组名是地址，就是一个常量的值
//    strcpy(arr2, arr1);
//   // my_strcpy(arr2, arr1);
//
//	printf("%s\n", arr2);
//
//	return 0;
//}
//

//strcat用于连接字符串
// 模拟实现
//char* my_strcat(char* dest, const char* src)
//{//	char* ret = dest;
//	assert(dest && src);
//	//1.找到目标空间中的\0
//	while (*dest != '\0')
//	{
//		dest++;
//	}
//	//2.数据的拷贝
//	while (*dest++ = *src++)
//	{
//		;
//	}
//	return ret;
//}
//
//int main()
//{
//	char arr1[20] = "abcdef";
//	//char arr2[20] = "ghi";
//
//	my_strcat(arr1, arr1);
//	printf("%s\n", arr1);
//
//	return 0;
//}

//strcap;
//int main()
//{
//	char arr1[20] = "abcdef";
//	//char arr2[20] = "ghi";
//
//	strcat(arr1, arr1);//如果要实现自己给自己追加，尽量不要使用strcat;
//	printf("%s\n", arr1);
//
//	return 0;
//}
//

//如果要比较两个字符串的内容，需要使用strcmp
//int main()
//{
//	//char arr1[] = "abcdef";
//	//char arr2[] = "abcdef";
//	//if (arr1 == arr2)//比较的是地址
//	//{
//	//}
//
//	//if ("abcdef" == "abc")//也是在比较地址
//	//{
//	//}
//
//	//如果要比较两个字符串的内容，需要使用strcmp
//
//	char arr1[] = "abq";
//	char arr2[] = "abcdef";
//	int ret = strcmp(arr1, arr2);
//	printf("%d\n", ret);
//	
//	return 0;
//}

//int my_strcmp(const char* str1, const char* str2)
//{
//	assert(str1 && str2);
//
//	while (*str1 == *str2)
//	{
//		if (*str1 == '\0')
//			return 0;
//		str1++;
//		str2++;
//	}
//	if (*str1 > *str2)
//	{
//		return 1;
//	}
//	else
//	{
//		return - 1;
//	}
//}
//
//int my_strcmp(const char* str1, const char* str2)
//{
//	assert(str1 && str2);
//
//	while (*str1 == *str2)
//	{
//		if (*str1 == '\0')
//			return 0;
//		str1++;
//		str2++;
//	}
//	return *str1 - *str2;
//}
//
//
//int main()
//{
//	char arr1[] = "abq";
//	char arr2[] = "abcdef";
//	int ret = my_strcmp(arr1, arr2);
//	printf("%d\n", ret);
//
//
//	return 0;
//}


//int main()
//{
//	char arr1[] = "abcdef";
//	char arr2[20] = "xxxxxxxxxx";
//	strncpy(arr2, arr1, 3);
//	printf("%s\n", arr2);
//
//	return 0;
//}

//int main()
//{
//	char arr1[] = "abcdef";
//	char arr2[20] = "xxxxxxxxxxxx";
//	int len = strlen(arr1);
//	strncat(arr2, arr1, len);
//
//	printf("%s\n", arr2);
//
//	return 0;
//}



//int main()
//{
//	char arr1[] = "abcdef";
//	char arr2[] = "abqdefghi";
//	int ret = strncmp(arr1, arr2, 2);
//	printf("%d\n", ret);
//
//	return 0;
//}

//int main()
//{
//	char arr1[10] = "abcdef";
//	char arr2[] = "abqdefghiqqq";
//	strcpy(arr1, arr2);//存在越界访问
//	printf("%s\n", arr1);
//	return 0;
//}

//strstr在一个字符串中查找另一个字符串
//int main()
//{
//	char arr1[] = "this is an apple\n";
//	//const char* p = "Appl";
//	char arr2[] = "is";
//	char* ret = strstr(arr1, arr2);
//
//	if (ret != NULL)
//	{
//		printf("%s\n", ret);
//	}
//	else
//	{
//		printf("找不到\n");
//	}
//
//	return 0;
//}



//
//简单的写法 - 暴力查找
//KMP算法 - 在字符串中找字符串- 更加高效，实现和理解也更复杂

//char* my_strstr(const char* str1, const char* str2)
//{
//	const char* s1 = NULL;
//	const char* s2 = NULL;
//	const char* cur = str1;
//
//	if (*str2 == '\0')
//		return (char*)str1;
//
//	while (*cur)
//	{
//		s1 = cur;
//		s2 = str2;
//		while (*s1 !='\0' && *s2!='\0' && *s1 == *s2)
//		{
//			s1++;
//			s2++;
//		}
//		if (*s2 == '\0')
//		{
//			return (char*)cur;
//		}
//		cur++;
//	}
//	return NULL;
//}
//
//
//int main()
//{
//	char arr1[] = "abbbcdef";
//	char arr2[] = "bbq";
//	char* ret = my_strstr(arr1, arr2);
//	if (ret == NULL)
//		printf("找不到\n");
//	else
//		printf("%s\n", ret);
//
//	return 0;
//}

//strtok函数
//int main()
//{
//	char arr[] = "zpengwei@yeah.net@hehe";
//	char arr2[30] = { 0 };//"zpengwei\0yeah\0net"
//	strcpy(arr2, arr);
//	const char* sep = "@.";//分隔符要写齐
//	char* ret = NULL;
//	for (ret = strtok(arr2, sep); ret != NULL; ret=strtok(NULL, sep))
//	{
//		printf("%s\n", ret);//这个写法很巧妙
//	}
//	
//	//ret = strtok(arr2, sep);
//	//printf("%s\n", ret);
//
//	//ret = strtok(NULL, sep);
//	//printf("%s\n", ret);
//
//	//ret = strtok(NULL, sep);
//	//printf("%s\n", ret);
//
//	return 0;
//}
//
//
////strerror函数
////int main()
////{
////	int i = 0;
////	for (i = 0; i <= 10; i++)
////	{
//	printf("%d:    %s\n", i, strerror(i));
////	}
////
////	return 0;
////}

#include <errno.h>

//int main()
//{
//	//fopen以读的形式打开文件的时候，如果文件不存在，就打开失败
//	FILE* pf = fopen("test.txt", "r");
//	if (pf == NULL)
//	{
//		printf("%s\n", strerror(errno));
//		return 1;
//	}
//	//读文件
//
//	//关闭文件
//	fclose(pf);
//
//	return 0;
//}
// 
// 


//perror
//int main()
//{
//	//fopen以读的形式打开文件的时候，如果文件不存在，就打开失败
//	FILE* pf = fopen("test.txt", "r");
//	if (pf == NULL)
//	{
//		perror("zhangsan");
//		return 1;
//	}
//	//读文件
//
//	//关闭文件
//	fclose(pf);
//
//	return 0;
//}









//内存函数
//memcpy-针对内存块进行拷贝
//模拟实现
#include<assert.h>
//void* my_memcpy(void* dest, const void* src, size_t num)
//{
//	int i = 0;
//	void* ret = dest;
//	assert(dest && src);
//	while (num--)
//	{
//		*(char*)dest = *(char*)src;//使用char*类型指针解引用1个字节
//		src = (char*)src + 1;
//		dest = (char*)dest + 1;
//	}
//	return ret;
//}
//int main()
//{
//	int arr[] = { 1,2,3,4,5,6,7,8,9,10 };
//	int arr2[20] = { 0};
//	//memcpy(arr2, arr, 20);//20指字节
//
//	my_memcpy(arr2, arr, 20);
//
//	int i = 0;
//	for (i = 0; i < 20; i++)
//	{
//		printf("%d ", arr2[i]);
//	}
//	return 0;
//}

//memcpy不期望重叠拷贝的，只需完成不重叠拷贝
//int main()
//{
//	int arr[] = { 1,2,3,4,5,6,7,8,9,10 };
//	int arr2[20] = { 0};
//	memcpy(arr+2, arr, 20);//20指字节
//
//	//my_memcpy(arr+2, arr, 20);
//
//	int i = 0;
//	for (i = 0; i < 20; i++)
//	{
//		printf("%d ", arr[i]);
//	}
//	return 0;
//}




//memmove函数（专门支持重叠）
//void* my_memmove(void* dest, const void* src, size_t num)
//{
//	assert(dest && src);
//	void* ret = dest;
//	if (dest < src)
//	{
//		while (num--)
//			*(char*)dest = *(char*)src;
//		dest = (char*)dest + 1;
//		src = (char*)src + 1;
//	}
//	else
//	{
//		while (num--)
//		{
//			*((char*)dest + num) = *((char*)src + num);
//		}
//	}
//}
//
//int main()
//{
//	int arr[] = { 1,2,3,4,5,6,7,8,9,10 };
//	//memmove(arr + 2, arr, 5 * sizeof(int));
//	my_memmove(arr + 2, arr, 5 * sizeof(int));
//
//	int i = 0;
//	for (i = 0; i < 10; i++)
//	{
//		printf("%d ", arr[i]);
//	}
//	return 0;
//}


//memset
// 设置字符
//int main()
//{
//	char arr[] = "hello world";
//	memset(arr, 'x', 5);//以字符的个数来设置
//	printf("%s", arr);
//	return 0;
//}

//设置整型
//int main()
//{
//	int arr[5] = {0};
//	memset(arr, 1, 20);//以字节来设置
//	int i = 0;
//	for (i = 0; i < 5; i++)
//	{
//		printf("%d", arr[i]);
//
//	}
//	return 0;
//}


//memcmp内存块的比较
//int main()
//{
//	int arr1[] = { 1,2,3,4,5,6,7 };
//	int arr2[] = { 1,2,3,4,8,8,8 };
//	int ret = memcmp(arr1, arr2, 16);
//	printf("%d", ret);
//	return 0;
//}



//数据在内存中的储存（说实话，这几个题多听几遍）
// 大小端练习（写博客）每个题目都要深刻的理解
// 练习一：
//设计一个小程序来判断当前机器的字节序
//int main()
//{
//	int n = 1;
//	if (*(char*)&n == 1)
//		printf("小端\n");
//	else
//		printf("大端\n");
//
//}

//练习二：（等那天你想理解的时候再听吧）
//int main()
//{
//	char a = -1;
//	signed char b = -1;//有符号
//	unsigned char c = -1;//无符号的
//	//总归char类型
//	printf("a=%d,b=%d,c=%d", a, b, c);
//	return 0;
//}

//练习三：
//int main()
//{
//	char a = -128;
//	//
//	printf("%u\n", a);//%u
//	return 0;
//}

//练习四：
//int main()
//{
//	char a = 128;
//	//00000000000000000000000000000100000000000
//	//10000000
//	//111111111111111111111000000
//	printf("%u\n", a);
//	return 0;
//}

//练习5
//int main()
//{
//	char a[1000];
//	int i;
//	for (i = 0; i < 1000; i++)
//	{
//		a[i] = -1 - i;
//	}
//	printf("%d", strlen(a));
//	return 0;
//}
//
//
//#include <stdio.h>
//unsigned char i = 0;
//int main()
//{
//	for (i = 0; i <= 255; i++)
//	{
//		printf("hello world\n");
//	}
//	return 0;
//}
//
//#include <stdio.h>
//int main()
//{
//	unsigned int i;
//	for (i = 9; i >= 0; i--)
//	{
//		printf("%u\n", i);
//	}
//	return 0;
//}


//#include <stdio.h>
//int main()
//{
//	int a[4] = { 1, 2, 3, 4 };
//	int* ptr1 = (int*)(&a + 1);
//	int* ptr2 = (int*)((int)a + 1);
//	printf("%x,%x", ptr1[-1], *ptr2);
//	return 0;
//}


//#include <stdio.h>
//int main()
//{
//	int n = 9;
//	float* pFloat = (float*)&n;
//	printf("n的值为：%d\n", n);
//	printf("*pFloat的值为：%f\n", *pFloat);
//	*pFloat = 9.0;
//	printf("num的值为：%d\n", n);
//	printf("*pFloat的值为：%f\n", *pFloat);
//	return 0;
//}



//struct S2
//{
//	char c1;//1
//	char c2;//1
//	int i;//4
//};
//printf("%d\n", sizeof(struct S2));
//
//
//struct S3
//{
//	double d;//double类型是8个字节
//	char c;//1
//	int i;//4
//};
//printf("%d\n", sizeof(struct S3));
//



//结构体传参(传地址）
//struct S
//{
//	int arr[4];
//	int a;
//	char t;
//};
//void print(struct S* tmp)
//{
//	for (int i = 0; i < 4; i++)
//	{
//		printf("%d", tmp->arr[i]);
//	}
//	printf("%d", tmp->a);
//	printf("%c", tmp->t);
//
//}
//
//int main()
//{
//	struct S s = { {1,2,3,4},1,'a'};
//	print(&s);
//	return 0;
//}

//位段(二进制位）式的结构
//位段是专门设置出来节省内存的
//不能对位段式成员取地址
//struct S
//{
//	int _a : 2;//例：只占两个比特位（节省空间）
//	int _b : 5;
//};
//int main()
//{
//	return 0;
//}


//联合体和枚举

//union Un
//{
//	char c;
//	int i;
//};
//
//int main()
//{
//	union Un n = { 0 };
//	printf("%zd\n", sizeof(n));//4
//
//	//联合体的特点
//	printf("%p\n", &n);
//	printf("%p\n", &(n.i));
//	printf("%p\n", &(n.c));//联合体也叫共用体，他们共用同一块空间
//	return 0;
//}

//联合体可以节省空间
//举例：
//⽐如，我们要搞⼀个活动，要上线⼀个礼品兑换单，礼品兑换单中有三种商品：图书、杯⼦、衬衫。
//每⼀种商品都有：库存量、价格、商品类型和商品类型相关的其他信息。
//图书：书名、作者、⻚数
//杯⼦：设计
//衬衫：设计、可选颜⾊、可选尺⼨
// 
//struct gift_list
//{
//	int stock_number;//库存量 
//	double price; //定价 
//	int item_type;//商品类型 
//
//	union {
//		struct
//		{
//			char title[20];//书名 
//			char author[20];//作者 
//			int num_pages;//⻚数 
//		}book;
//		struct
//		{
//			char design[30];//设计 
//		}mug;
//		struct
//		{
//			char design[30];//设计 
//			int colors;//颜⾊ 
//			int sizes;//尺⼨ 
//		}shirt;
//	}item;
//};
//
//int main()
//{
//	struct gift_list gl;
//	gl.item.book;//找信息，其他同理
//	return 0;
//}

//枚举类型
//枚举——列举
//现实生活中，一周，男女，月份有12个月都可以一一列举
//enum Color
//{
//	//三原色的可能取值-枚举常量，默认情况下是0，1，2，在这里面可以改值
//	RED=5,
//	GREEN,
//	BLUE
//};
////性别
//enum Sex
//{
//	MALE,
//	FAMALE,
//	SECERT
//};
//int main()
//{
//	enum Color color = RED;
//	printf("%d\n", RED);
//	printf("%d\n", GREEN);
//	printf("%d\n", BLUE);
//
//	return 0;
//}

//枚举类型的优点
//enum Option
//{
//	EXIT,
//	ADD,
//	SUB,
//	MUL,
//	DIV
//};
//
////用枚举写菜单
//int main()
//{
//	int input = 0;
//	printf("请选择：");
//	scanf("%d", &input);
//	switch (input)
//	{
//	case 1:
//		break;
//	case 2:
//		break;
//	case 3:
//		break;
//	case 4:
//		break;
//	case 0:
//		default;
//	}
//	return 0;
//}

//为什么使用枚举？
//我们可以使用#define定义常量，为什么⾮要使⽤枚举？
//枚举的优点：
//1. 增加代码的可读性和可维护性
//2. 和#define定义的标识符⽐较枚举有类型检查，更加严谨。
//3. 便于调试，预处理阶段#define会删除定义的符号
//4. 使⽤⽅便，⼀次可以定义多个常量
//5. 枚举常量是遵循作⽤域规则的，枚举声明在函数内，只能在函数内使⽤



//动态内存管理
//1.为什么要有动态内存管理？
//想让程序员能够灵活的控制内存空间。


//2.4个函数
//malloc free calloc realloc


//1)malloc-这个函数向内存申请⼀块连续可⽤的空间，并返回指向这块空间的指针。
//• 如果开辟成功，则返回⼀个指向开辟好空间的指针。
//• 如果开辟失败，则返回⼀个 NULL 指针，因此malloc的返回值⼀定要做检查。
//• 返回值的类型是 void* ，所以malloc函数并不知道开辟空间的类型，具体在使⽤的时候使⽤者⾃
//⼰来决定。
//• 如果参数 size 为0，malloc的⾏为是标准是未定义的，取决于编译器。
//#include<stdlib.h>
//int main()
//{
//	int* p = (int*)malloc(20);
//	if (p == NULL)
//	{
//		perror("mallloc");
//		return;
//	}
//	//使用空间
//	int i = 0;
//	for (i = 0; i < 5; i++)
//	{
//		*(p + i) = i + 1;
//	}
//	//释放空间
//	free(p);
//	p = NULL;//避免p成为野指针
//	return 0;
//}

//calloc,在申请空间的同时，会将空间的每个元素初始化为0；
//int main()
//{
//
//	int* p = (int*)calloc(5, sizeof(int));//其他同malloc;
//	return 0;
//}
//
//

//realloc
//int main()
//{
//	int* p = (int*)malloc(20);
//	if (p == NULL)
//	{
//		perror("mallloc");
//		return 1;
//	}
//	
//		for (int i = 0; i < 5; i++)
//	{
//		*(p + i) = i + 1;
//	}
//		//希望将空间调整为40个字节（图片解析）
//		int* ptr = (int*)realloc(p, 40);
//		if (ptr != NULL)
//		{
//			p = ptr;
//			//……
//		}
//		else
//		{
//			perror("realloc");
//			//……
//		}
//   return 0;
//}
////realloc函数的特殊的用法
//int main()
//{
//	realloc(NULL, 10);//==malloc(10); 
//	return 0;
//}


//常见的动态内存的错误
//1.对空指针的解引用操作
//因为malloc的返回值是需要判断的
//int main()
//{
//	int* p = (int*)malloc(20);
//	if (p == NULL)
//	{
//		perror("malloc");
//		return 1;
//	}
//	//或者assert(p);
//	return 0;
//}
//
////2.对动态开辟空间的越界访问
//void test()
//{
//	int i = 0;
//	int* p = (int*)malloc(10 * sizeof(int));
//	if (NULL == p)
//	{
//		exit(EXIT_FAILURE);
//	}
//	for (i = 0; i <= 10; i++)
//	{
//		*(p + i) = i;//当i是10的时候越界访问 
//	}
//	free(p);
//}
////3.对非动态开辟的free释放
//int main()
//{
//	int a = 10;
//	int*p = &a;
//	//……
//	free(p);
//	p = NULL;
//
//	return 0;
//}
////4.使用free释放一块动态开辟内存的一部分
//int main()
//{
//	int* p = (int*)malloc(100);
//	if (p == NULL)
//	{
//		return 1;
//	}
//	int i = 0;
//	for (i = 0; i < 5; i++)
//	{
//		*p = i + 1; p++;
//	}
//	free(p);//这里的p不再是动态申请的内存的起始位置。
//	p = NULL;
//	return 0;
//}
//
////5.对同一块空间多次使用
//void test()
//{
//	int* p = (int*)malloc(100);
//	free(p);
//	free(p);//重复释放 
//}
//
//int main()
//{
//	test();
//	return 0;
//}
//
////6.动态内存忘记释放。
////造成内存泄漏的问题
////内存是一种资源，你申请后不用了，要记住回收，我记得老师说过一次内存泄漏，要带走年终奖
//void test()
//{
//	int* p = (int*)malloc(100);
//	if (NULL != p)
//	{
//		*p = 20;
//	}
//}
//
//int main()
//{
//	test();
//	while (1);
//}
//

//malloc/calloc/realloc申请的内存，如果不想使用的时候，可以使用free释放。     //和free要成对出现+
// 
//如果没有使用free释放，当程序运行结束的时候，也会由操作系统回收的！
//尽量要做到谁申请的空间，谁释放。
//如果不能释放，要告诉使用的人，记得释放。

//动态内存经典笔试题
// 1.//？（指针内容要学）
//void GetMemory(char* p)
//{
//	p = (char*)malloc(100);
//}
//void Test(void)
//{
//	char* str = NULL;
//	GetMemory(str);
//	strcpy(str, "hello world");
//	printf(str);
//}
//2.
//char* GetMemory(void)
//{
//	char p[] = "hello world";
//	return p;
//}
//void Test(void)
//{
//	char* str = NULL;
//	str = GetMemory();//1.等GetMemory函数返回后，使用str指针去访问p数组，就是非法访问，因为p数组的内存已经还给了操作系统。str是野指针。
//	printf(str);
//}
//
//int main()
//{
//	Test();
//	return 0;
//}

//3.
//void GetMemory(char** p, int num)
//{
//	*p = (char*)malloc(num);
//}
//void Test(void)
//{
//	char* str = NULL;
//	GetMemory(&str, 100);
//	strcpy(str, "hello");
//	printf(str);//没有free
//}
//int main()
//{
//	Test();
//	return 0;
//}
//

//void Test(void)
//{
//	char* str = (char*)malloc(100);
//	strcpy(str, "hello");
//	free(str);//释放的是空间，但是指向的地址还在
//	if (str != NULL)
//	{
//		strcpy(str, "world");//这里空间不见了，非法访问内存
//		printf(str);
//	}
//}
//
//int main()
//{
//	Test();
//	return 0;
//}




//柔性数组
//c99中，结构中的最后一个元素是允许是未知大小的数组，这叫做“柔性数组”成员
//struct s
//{
//	char c;
//	int n;
//	int arr[];//柔性数组：结构体中的最后一个成员；而且大小未指定；
//};
////或者
//struct s
//{
//	char c;
//	int n;
//	int arr[0];//柔性数组：结构体中的最后一个成员；而且大小未指定；
//};
//
////特点：
////柔性数组前面至少有一个其他成员
////sizeof计算结构体的大小，不包含柔性数组的内存
////包含柔性数组成员的结构用malloc()函数进行内存分配，并且分配的内存应该大于结构的大小，以适应柔性数组的预期大小
//
//struct S
//{
//	int n;
//	int arr[];
//};
//
//int main()
//{
//	struct S* ps = (struct S*)malloc(sizeof(struct S) + 5 * sizeof(int));
//}



//文件操作
// 
// 
// 
// 
// 
//int main()
//{
//	int n = 0;
//	scanf("%d", &n);
//	printf("%d", n);
//	return 0;
//}


//文件的基本操作
//打开文件
//读/写文件
//关闭文件

//int main()
//{
//	FILE* pf = fopen("test.txt", "r");
//	if (pf == NULL)
//	{
//		perror("fopen");
//		return 1;//异常返回
//	}
//	fclose(pf);
//	pf = NULL;
//	return 0;
//}
//
//
//
//int main()
//{
//	//打开文件
//	FILE* pf = fopen("test.txt", "w");
//	if (pf == NULL)
//	{
//		perror("fopen");
//		return 1;//异常返回
//	}
//	//写文件（一个字符）
//	char cha = 0;
//	for (cha = 'a'; cha <= 'z'; cha++)
//	{
//		fputc(cha, pf);
//	}
//	//写字符串
//	fputs("hello world\n", pf);
//	
//	//关闭文件
//	fclose(pf);
//	pf = NULL;
//
//	return 0;
//}


//
//int main()
//{
//	//打开文件
//	FILE* pf = fopen("test.txt", "r");
//	if (pf == NULL)
//	{
//		perror("fopen");
//		return 1;//异常返回
//	}
//	
//	//读文件（一个字符）
//	/*int ch = fgetc(pf);
//	printf("%c\n", ch);*/
//	//读取全部
//	/*int ch = 0;
//	while ((ch = fgetc(pf)) != EOF)
//	{
//		printf("%c", ch);
//
//	}*/
//
//
//
//	////读字符串
//	char arr[10] = { 0 };
//	//fgets(arr, 10, pf);//只读一行，也会读取\n,自动加\0
//	//printf("%s", arr);
//	
//	////循环读字符串
//
//	while (fgets(arr, 10, pf) != NULL)
//	{
//		printf("%s", arr);
//	}
//	
//	//关闭文件
//	fclose(pf);
//	pf = NULL;
//
//	return 0;
//}
//
////明天到C的网站上查找fputc(),fputs(),fgetc(),fgets()的用法。
//
//struct S
//{
//	char name[10];
//	int age;
//	float score;
//};
//int main()
//{
//	struct S s = { "张三",20,2.3 };
//	//想把s的数据存放在文件中
//	FILE* pf = fopen("test.txt", "w");
//	if (pf == NULL)
//	{
//		perror("fopen");
//		return 1;
//	}
//	//文件 以文本的形式打印出来
//	fprintf(pf, "%s %d %f", s.name, s.age, s.score);
//
//	fclose(pf);
//	pf = NULL;
//	return 0;
//}
//
//
//struct S
//{
//	char name[10];
//	int age;
//	float score;
//};
//int main()
//{
//	struct S s = { "张三",20,2.3 };
////	想从文件test.txt中读取数据放在s中
//	FILE* pf = fopen("test.txt", "r");
//	if (pf == NULL)
//	{
//		perror("fopen");
//		return 1;
//	}
//	//读文件
//	fscanf(pf, "%s %d %f", s.name, &(s.age), &(s.score));
//	//打印在屏幕上
//	printf( "%s %d %f", s.name, s.age, s.score);
//
//	fclose(pf);
//	pf = NULL;
//	return 0;
//}


//struct S
//{
//	char name[10];
//	int age;
//	float score;
//};
//int main()
//{
//	char buf[200] = { 0 };
//	struct S s = { "张三",20,2.3 };
//	sprintf(buf, "%s %d %f", s.name, s.age, s.score);//把s转换成字符串形式
//	printf("%s",buf);
//
//	struct S t = { 0 };
//	sscanf(buf, "%s %d %f", t.name, &(t.age), &(t.score));//
//	printf("%s %d %f", t.name, t.age, t.score);
//
//
//
//	return 0;
//}



//int main()
//{
//	int arr[] = { 1,2,3,4 };
//	int sz = sizeof(arr) / sizeof(arr[0]);
//	FILE* pf = fopen("test.txt", "wb");
//	if (pf == NULL)
//	{
//		perror("fopen");
//		return 1;
//	}
//	//写数据（以二进制的形式写数据）
//	fwrite(arr, sizeof(arr[0]), sz, pf);
//	fclose(pf);
//	pf = NULL;
//	return 0;
//}
//
//
//int main()
//{
//	int arr[4] = { 1,2,3,4 };
//	FILE* pf = fopen("test.txt", "rb");
//	if (pf == NULL)
//	{
//		perror("fopen");
//		return 1;
//	}
//	//读数据(以二进制的形式读数据）
//	//fread(arr, sizeof(arr[0]), 4, pf);
//	/*int i = 0;
//	for (i = 0; i < 4; i++)
//	{
//		printf("%d", arr[i]);
//	}*/
//	//或者
//	int i = 0;
//	while (fread(arr+i, sizeof(int), 1, pf))
//	{
//		printf("%d", arr[i]);
//	}
//	fclose(pf);
//	pf = NULL;
//	return 0;
//}



//int main()
//{
//	FILE* pf = fopen("test.txt", "r");
//	if (pf == NULL)
//	{
//		perror("fopen");
//		return 1;
//	}
//	//读文件
//	int ch = fgetc(pf);
//	printf("%c\n", ch);
//
//	fseek(pf,1,SEEK_CUR);//从文件指针当前的位置向后偏移一个。
//	ch = fgetc(pf);
//	printf("%c\n", ch);
//
//	fseek(pf, -1, SEEK_END);//从文件指针当前的位置向前偏移一个。
//	 ch = fgetc(pf);
//	printf("%c\n",ch );
//
//
//
//	fclose(pf);+
//	pf = NULL;
//	return 0;
//}



//int main()
//{
//	FILE* pf = fopen("test.txt", "r");
//	if (pf == NULL)
//	{
//		perror("fopen");
//		return 1;
//	}
//	//读文件
//	int ch = fgetc(pf);
//	printf("%c\n", ch);
//
//	fseek(pf, 1, SEEK_CUR);//从文件指针当前的位置向后偏移一个。
//	printf("%d", ftell(pf));//相对返回起始位置的偏移量
//	rewind(pf);
//	ch = fgetc(pf);
//	printf("%c\n", ch);
//	fclose(pf);
//	pf = NULL;
//	return 0;
//}
//
//int main()
//{
//	FILE* pf = fopen("test.txt", "r");
//	if (pf == NULL)
//	{
//		perror("fopen");
//		return 1;
//	}
//	//读取
//	int ch = 0;
//	while (ch = fgetc(pf) != EOF)
//	{
//		printf("%c\n", ch);
//	}
//	//判断是什么原因导致读取结束的
//	if (feof(pf))
//	{
//		printf("遇到文件末尾，读取正常结束\n");
//	}
//	else if (ferror(pf))
//	{
//		perror("fgetc");
//	}
//	return 0;
//}

//拷贝文件
// 
//int main()
//{
//	FILE* pfin = fopen("test1.txt", "r");
//	if (pfin == NULL)
//	{
//		perror("fopen:test1.txt");
//		return 1;
//	}
//	FILE* pfout = fopen("test2.txt", "w");
//	if (pfout == NULL)
//	{
//		fclose(pfin);
//		perror("fopen:test2.txt");
//		return 1;
//	}
//	//读文件和写文件
//	int ch = 0;
//	while ((ch = fgetc(pfin)) != EOF)
//	{
//	   fputc(ch, pfout);
//	 }
//	fclose(pfin);
//	pfin = NULL;
//	fclose(pfout);
//	pfout = NULL;
//
//	return 0;
//}
//#include <windows.h>
////VS2019 WIN11环境测试 
//int main()
//{
//	FILE* pf = fopen("test.txt", "w");
//	fputs("abcdef", pf);//先将代码放在输出缓冲区 
//	printf("睡眠10秒-已经写数据了，打开test.txt⽂件，发现⽂件没有内容\n");
//	Sleep(10000);
//	printf("刷新缓冲区\n");
//	fflush(pf);//刷新缓冲区时，才将输出缓冲区的数据写到⽂件（磁盘） 
//	//注：fflush 在⾼版本的VS上不能使⽤了 
//	printf("再睡眠10秒-此时，再次打开test.txt⽂件，⽂件有内容了\n");
//	Sleep(10000);
//	fclose(pf);
//	//注：fclose在关闭⽂件的时候，也会刷新缓冲区 
//	pf = NULL;
//	return 0;
//}
//



//预处理详解
//int main()
//{
//	printf("%s\n", __FILE__);
//	printf("%s\n", __DATE__);
//	printf("%s\n", __TIME__);
//	printf("%s\n", __LINE__);
//
//#define DEBUG_PRINT printf("file:%s\tline:%d\t \
// date:%s\ttime:%s\n" ,\ //续行符
// __FILE__,__LINE__ , \
// __DATE__,__TIME__ ) 
//
//
//	return 0;
//}


//宏

//#define SQUARE(x) x*x
//int main()
//{
//	int a = 5;
//	int ret = SQUARE(a);
//	printf("%d\n", ret);
//
//	return 0;
//}



//求两个数的较大值
//#define MAX(X,Y) ((X)>(Y)?(X):(Y))




//#define Print(n,format) printf("the value of "#n" is format",n);
//int main()
//{
//	int a = 1;
//	Print(a, "%d");
//	//printf("the value of a is %d\n", a);
//
//	float f = 5.6f;
//	Print(f,"%f");
//	///printf("the value of f is %f\n", f);
//
//	return 0;
//}


//#define MAX 100
//int main()
//{
//	printf("%d\n", MAX);
//#undef MAX
//	return 0;
//}


//int main()
//{
//#if 0
//	printf("hh");
//#endif
//	return 0;
//}

//#define M 1
//
//int main()
//{
//#if M==0
//	printf("hhp");
//#elif M==1
//	printf("hh");
//#else
//	printf("ok");
//#endif
//}

//#define MAX 
//int main()
//{
//#ifdef MAX
//	printf("hh");
//#endif
//	return 0;
//}
//
